﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Linq.Expressions;

namespace aQuery
{
    public class ActionObserver<T> : ActionReaderChild<T> where T : class, INotifyPropertyChanged
    {

        #region internal members
        internal List<Expression<PropertyOfTarget<T>>> Properties { get; private set; }

        internal ActionObserver(ActionReader<T> target, Expression<PropertyOfTarget<T>> property)
            : base(target)
        {
            Properties = new List<Expression<PropertyOfTarget<T>>>() { property };

            this._watching = false;
        }

        internal void StartWatching()
        {
            if (!_watching)
            {
                _watching = true;
                Target.PropertyChanged += Target_PropertyChanged;
            }
        }

        internal void StopWatching()
        {
            if (_watching)
            {
                _watching = false;
                Target.PropertyChanged -= Target_PropertyChanged;
            }
        }
        #endregion

        #region private members
        private bool _watching;

        private void Target_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            foreach (var expression in Properties)
            {
                testExpressionWithProperty(expression, e.PropertyName);
            }
        }

        private void testExpressionWithProperty(Expression<PropertyOfTarget<T>> expression, String propertyName)
        {
            var unary = expression.Body as UnaryExpression;

            if (unary != null)
            {
                var prop = unary.Operand as MemberExpression;

                if (prop != null)
                {
                    testAsMemberExpression(prop, propertyName);
                }
                else
                {
                    testAsBinaryExpression(unary.Operand as BinaryExpression, expression, propertyName);
                }
            }
        }

        private void testAsMemberExpression(MemberExpression expression, String propertyName)
        {
            if (expression.Member.Name == propertyName)
            {
                ActionReader.Execute();
            }
        }

        private void testAsBinaryExpression(BinaryExpression expression, Expression<PropertyOfTarget<T>> mainExpression, String propertyName)
        {
            var right = expression.Right as MemberExpression;
            var left = expression.Left as MemberExpression;

            if ((left != null && left.Member.Name == propertyName) ||
                (right != null && right.Member.Name == propertyName))
            {
                if ((Boolean)(mainExpression.Compile().Invoke(Target)) == true)
                {
                    ActionReader.Execute();
                }
            }
        }
        #endregion

    }
}
